/****************************************************************************
 * Copyright (C) 2011
 * by Dj_Skual
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * savebrowser.cpp
 *
 * for SaveGame Manager GX 2011
 ***************************************************************************/
#include <algorithm>
#include <wctype.h>

#include "../../XML/GameTDB.hpp"
#include "../../Saves/SaveTools.h"
#include "../../System/nandtitle.h"
#include "../../FileOperations/Nand.hpp"
#include "savebrowser.h"

SaveBrowser::SaveBrowser()
	:Browser()
{
	SearchChars.clear();
	
	//!Reset and prepare browser
	ResetBrowser();
	
	GetList();
}

SaveBrowser::~SaveBrowser()
{
	ResetBrowser();
}

void SaveBrowser::ResetBrowser()
{
	for(u32 i = 0; i < browserList.size(); ++i)
		delete browserList.at(i);
	
	browserList.clear();
	FileFilter. clear();
	
	browserSelIndex = -1;
}

void SaveBrowser::AddEntry()
{
	SaveStruct * newSave = new SaveStruct;
	newSave->name = L"";
	newSave->subname = L"";
	newSave->ID = "";
	newSave->tid = 0;
	newSave->blocksize = 0;
	newSave->isNotInstalled = false;

	browserList.push_back(newSave);
}

void SaveBrowser::SetSelectedIndex(int ind)
{
	if(browserSelIndex == ind)
		return;
	
	browserSelIndex = ind;
	IndexChanged();
}

void SaveBrowser::LoadTitlesFromGameTDB()
{
	if(!Settings.GameTDBPath || !Settings.GameTDBTitles)
		return;
	
	std::wstring Title;
	std::string gameTDBPath = Settings.GameTDBPath;
	gameTDBPath += "/wiitdb.xml";
	
	GameTDB * XML_DB = new GameTDB(gameTDBPath.c_str());
	XML_DB->SetLanguageCode(Settings.GameTDBLanguageCode);

	for(u32 i = 0; i < browserList.size(); ++i)
	{
		if(XML_DB->GetTitle(browserList.at(i)->ID.c_str(), Title))
			browserList.at(i)->name = Title;
	}
	
	delete XML_DB;
}

static bool NameSortCallback(const SaveStruct *a, const SaveStruct *b)
{
	return (stricmp((wstr2str(((SaveStruct *)a)->name)).c_str(), (wstr2str(((SaveStruct *)b)->name)).c_str()) < 0);
}

void SaveBrowser::GetList()
{
	ResetBrowser();
	
	//! Get title list
	if(NandTitles.Get() < 0)
		return;
	
	u32 titleCnt = NandTitles.Count();
	if(!titleCnt)
		return;
	
	int cnt = 0;
	for (u32 i = 0; i < titleCnt; i++)
	{
		u64 tid = NandTitles.At(i);
		
		//! Generate dirpath
		std::string savepath = SaveTools::GetNandPath(tid);
		
		//! Check for title savegame
		if (SaveTools::CheckTitle(savepath))
		{
			if(i >= browserList.size())
				AddEntry();
			
			cnt = browserList.size()-1;
			
			//! Set title name & subname
			browserList.at(cnt)->name = SaveTools::GetNandName(tid);
			browserList.at(cnt)->subname = SaveTools::GetNandSubname(tid);
			
			//! Set title ID[4]
			browserList.at(cnt)->ID = SaveTools::titleText((u32)(tid >> 32),(u32)(tid & 0xFFFFFFFF));
			
			//! Set titleid
			browserList.at(cnt)->tid = tid;
			
			//! Set title blockSize
			browserList.at(cnt)->blocksize = ((float)(Nand::GetDirSize(savepath)/(float)NAND_BLOCK_SIZE));
		}
	}
	
	//! set GameTDB Titles
	LoadTitlesFromGameTDB();
	
	if(browserList.size() > 1)
		std::sort(browserList.begin(), browserList.end(), NameSortCallback);
}

int SaveBrowser::FilterList(std::wstring fileFilter)
{
	if(!fileFilter.empty() && (fileFilter.size() == FileFilter.size()))
		return browserList.size();
	
	if(fileFilter.size() < FileFilter.size())
		GetList();
	
	if(browserList.empty())
		return 0;
	
	if(!fileFilter.empty())
		FileFilter = fileFilter;
	else
		FileFilter.clear();

	SearchChars.clear();
	
	browserSelIndex = 0;
	
	for (u32 i = 0; i < browserList.size(); ++i)
	{
		std::wstring Filename = browserList.at(i)->name;
		if (Filename.empty())
		{
			browserList.erase(browserList.begin()+i);
			i--;
			continue;
		}
		
		if(Settings.SearchMode == SEARCH_BEGINNING)
		{
			if (FileFilter.size() > 0 && wcsncasecmp(Filename.c_str(), FileFilter.c_str(), FileFilter.size()) != 0)
			{
				browserList.erase(browserList.begin()+i);
				i--;
				continue;
			}
			
			if (Filename.size() > FileFilter.size() &&
				SearchChars.find(towupper(Filename[FileFilter.size()])) == SearchChars.end() &&
				SearchChars.find(towlower(Filename[FileFilter.size()])) == SearchChars.end())
			{
				SearchChars.insert(Filename[FileFilter.size()]);
			}
		}
		else if(Settings.SearchMode == SEARCH_CONTENT)
		{
			if(!FileFilter.empty())
			{
				if (wcscasestr(Filename.c_str(), FileFilter.c_str()) == 0)
				{
					browserList.erase(browserList.begin()+i);
					i--;
					continue;
				}
				
				const wchar_t *found = Filename.c_str();
				while((found = wcscasestr(found, FileFilter.c_str())) != 0)
				{
					found += FileFilter.size();
					wchar_t ch = towupper(*found);
					if(ch)
						SearchChars.insert(ch);
				}
			}
			else
			{
				for(const wchar_t *wPtr = Filename.c_str(); *wPtr != 0; wPtr++)
				{
					wchar_t ch = towupper(*wPtr);
					if(ch > L'@')
						SearchChars.insert(ch);
				}
			}
		}
	}

	if (browserList.size() < 2)
		SearchChars.clear();
	
	ListChanged();
	IndexChanged();
	
	return browserList.size();
}
